package com.rxh.service;

import com.rxh.cache.*;
import com.rxh.exception.PayException;
import com.rxh.i18.I18Component;
import com.rxh.pojo.core.*;
import com.rxh.pojo.cross.BankResult;
import com.rxh.pojo.merchant.*;
import com.rxh.pojo.payment.CrossResult;
import com.rxh.pojo.payment.Trade;
import com.rxh.pojo.payment.TradeObject;
import com.rxh.pojo.risk.RiskOrderTrack;
import com.rxh.risk.RiskContext;
import com.rxh.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * User: 陈俊雄
 * Date: 2018/8/21
 * Time: 10:30
 * Project: Management
 * Package: com.rxh.service
 */
@Service
public class PaymentService {
    private final static Logger logger = LoggerFactory.getLogger(PaymentService.class);
    @Resource
    private TransactionService transactionService;

    private final MerchantWebSiteCache merchantWebSiteCache;
    private final MerchantSettingCache merchantSettingCache;
    private final MerchantPayCache merchantPayCache;
    private final MerchantRateCache merchantRateCache;
    private final MerchantRatePercentCache merchantRatePercentCache;
    private final CoreAcquirerCache coreAcquirerCache;
    private final CoreAcquirerAccountCache coreAcquirerAccountCache;
    private final CoreAcquirerRateCache coreAcquirerRateCache;
    private final CoreTradeRateCache coreTradeRateCache;
    private final RecordService recordService;
    private final RiskAnalysisService riskAnalysisService;
    private final I18Component i18Component;
    private final MerchantNotifyService merchantNotifyService;

    // 全局订单对象
    private CoreOrder order;
    // 全局商户支付信息对象
    private MerchantPay merchantPay;
    private CoreMerchantOrder merchantOrder;
    private CoreOrderDetail orderDetail;
    private CoreAcquirerAccount acquirerAccount;

    @Autowired
    public PaymentService(CoreAcquirerRateCache coreAcquirerRateCache, MerchantWebSiteCache merchantWebSiteCache, MerchantSettingCache merchantSettingCache, MerchantPayCache merchantPayCache, MerchantRateCache merchantRateCache, MerchantRatePercentCache merchantRatePercentCache, CoreAcquirerCache coreAcquirerCache, RiskAnalysisService riskAnalysisService, CoreAcquirerAccountCache coreAcquirerAccountCache, CoreTradeRateCache coreTradeRateCache, RecordService recordService, I18Component i18Component, MerchantNotifyService merchantNotifyService) {
        this.coreAcquirerRateCache = coreAcquirerRateCache;
        this.merchantWebSiteCache = merchantWebSiteCache;
        this.merchantSettingCache = merchantSettingCache;
        this.merchantPayCache = merchantPayCache;
        this.merchantRateCache = merchantRateCache;
        this.merchantRatePercentCache = merchantRatePercentCache;
        this.coreAcquirerCache = coreAcquirerCache;
        this.riskAnalysisService = riskAnalysisService;
        this.coreAcquirerAccountCache = coreAcquirerAccountCache;
        this.coreTradeRateCache = coreTradeRateCache;
        this.recordService = recordService;
        this.i18Component = i18Component;
        this.merchantNotifyService = merchantNotifyService;
    }

    /**
     * 内嵌支付
     *
     * @param orderTrack  订单追踪对象
     * @param tradeObject 交易信息对象
     * @return 返回交易信息
     * @throws PayException 支付异常信息
     */
    public String inlinePayment(RiskOrderTrack orderTrack, TradeObject tradeObject) throws PayException {
        BankResult bankResult = payment(orderTrack, tradeObject);
        return getInnerReturnMsg(bankResult);
    }

    /**
     * 跳转支付
     *
     * @param orderTrack  订单追踪对象
     * @param tradeObject 交易信息对象
     * @return 跳转支付结果对象
     * @throws PayException 支付异常信息
     */
    public CrossResult crossPayment(RiskOrderTrack orderTrack, TradeObject tradeObject) throws PayException {
        BankResult bankResult = payment(orderTrack, tradeObject);
        CrossResult crossResult = new CrossResult();
        crossResult.setOrderId(order.getId());
        crossResult.setBankCode(bankResult.getBankCode());
        crossResult.setBankStatus(bankResult.getStatus());
        crossResult.setMd5Info(merchantNotifyService.getNotifyMd5Info(order, merchantPay, bankResult));
        return crossResult;
    }

    /**
     * 支付逻辑
     *
     * @param orderTrack  订单追踪对象
     * @param tradeObject 交易信息对象
     * @return 银行结果对象
     * @throws PayException 支付异常信息
     */
    public BankResult payment(RiskOrderTrack orderTrack, TradeObject tradeObject) throws PayException {
        // 检查商户订单是否存在
        checkOrderExist(tradeObject.getMerNo(), tradeObject.getBillNo());
        /*
         获取交易所需对象
         */
        // 获取商户网址
        MerchantWebSite merchantWebSite = merchantWebSiteCache.getByMerchantIdAndSiteUrl(tradeObject.getMerNo(), orderTrack.getRefer());
        merchantWebSiteCache.check(merchantWebSite);
        orderTrack.setSiteId(merchantWebSite.getId());
        // 检查商户配置
        merchantSettingCache.check(merchantSettingCache.getByMerchantId(tradeObject.getMerNo()));
        // 获取商户支付对象
        merchantPay = merchantPayCache.getBySiteIdOrMerchantId(merchantWebSite.getId(), tradeObject.getMerNo(), tradeObject.getCurrency());
        // 交易Md5验证
        ParseTradeInfo.checkMd5(tradeObject, merchantPay.getSecretKey());
        // 获取商户扣率
        MerchantRate merchantRate = merchantRateCache.getBySiteIdOrMerchantId(merchantWebSite.getId(), tradeObject.getMerNo(), tradeObject.getPayMode(), tradeObject.getPayType());
        // 获取商户通道上抛对象
        MerchantRatePercent merchantRatePercent = merchantRatePercentCache.getByMerchantIdAndSiteIdAndCurrency(tradeObject.getMerNo(), merchantWebSite.getId(), tradeObject.getCurrency());
        // 获取收单机构
        CoreAcquirer acquirer = coreAcquirerCache.getById(merchantRate.getAcquirerId());
        coreAcquirerCache.check(acquirer, tradeObject.getCurrency());
        // 获取收单账户
        acquirerAccount = coreAcquirerAccountCache.getById(merchantRate.getAccountId());
        coreAcquirerAccountCache.check(acquirerAccount);
        // 获取通道扣率
        CoreAcquirerRate acquirerRate = coreAcquirerRateCache.getByAcquirerIdAndPayModeAndPayType(merchantRate.getAcquirerId(), tradeObject.getPayMode(), tradeObject.getPayType());
        coreAcquirerRateCache.check(acquirerRate);
        // 获取交易汇率
        CoreTradeRate tradeRate = coreTradeRateCache.getByTargetCurrency(tradeObject.getCurrency(), SystemConstant.CURRENCY_CNY);
        coreTradeRateCache.check(tradeRate);
        /*
         保存并获取对象
          */
        // 订单对象
        order = recordService.saveAndGetOrder(tradeObject, orderTrack, merchantWebSite, tradeRate, acquirerAccount);
        // 商户订单对象
        merchantOrder = recordService.saveAndGetMerchantOrder(tradeObject, order, merchantRate);
        CoreMerchantOrderRmb merchantOrderRmb = recordService.saveAndGetMerchantOrderRmb(tradeObject, order, merchantRate, tradeRate);
        // 订单详情对象
        orderDetail = recordService.saveAndGetOrderDetail(tradeObject, orderTrack, order);
        // 持卡人对象
        CoreCardholder cardholder = recordService.saveAndGetCardholder(tradeObject, order);
        // 持卡人信息对象
        CoreCardDetail cardDetail = recordService.saveAndGetCardDetail(tradeObject, cardholder);
        // 订单通道信息对象
        ChannelOrderInfo channelOrderInfo = recordService.saveAndGetChannelOrderInfo(order, tradeRate, merchantRatePercent, acquirerAccount, acquirerRate);
        // 保存订单商品信息
        List<CoreOrderGoods> orderGoodsList = recordService.saveAndGetOrderGoods(tradeObject.getProductDetails(), order);
        // 保存商户商品信息
        List<MerchantGoods> merchantGoodsList = recordService.saveAndGetMerchantGoods(tradeObject, merchantWebSite);
        /*
        执行风控
         */
        orderTrack.setStatus(SystemConstant.PAY_STATUS_RISK);
        RiskContext riskContext = new RiskContext();
        riskContext.setTradeObject(tradeObject);
        riskContext.setCoreAcquirer(acquirer);
        riskContext.setCoreAcquirerAccount(acquirerAccount);
        riskContext.setMerchantWebSite(merchantWebSite);
        riskContext.setCardholder(cardholder);
        riskContext.setCoreOrder(order);
        riskAnalysisService.doRisk(riskContext);
        // 发送交易
        Trade trade = new Trade();
        // ↓设置发往通道对象↓
        trade.setTradeObject(tradeObject);
        trade.setCoreOrder(order);
        trade.setCoreAcquirer(acquirer);
        trade.setCoreAcquirerAccount(acquirerAccount);
        trade.setCoreCardDetail(cardDetail);
        trade.setCoreMerchantOrder(merchantOrder);
        trade.setCoreOrderDetail(orderDetail);
        trade.setProductDetails(tradeObject.getProductDetails());
        trade.setCoreCardholder(cardholder);
        trade.setChannelOrderInfo(channelOrderInfo);
        // 发送交易
        orderTrack.setStatus(SystemConstant.PAY_STATUS_SEND);
        // BankResult bankResult = toPay(trade);
        BankResult bankResult = new BankResult();
        bankResult.setBankResult("交易成功 test");
        bankResult.setStatus(SystemConstant.BANK_STATUS_SUCCESS);
        bankResult.setBankCode("pay.1047");
        bankResult.setOrderId(order.getId());
        bankResult.setTime(new Date());
        bankResult.setParam("hello");
        bankResult.setBankOrderId("1000");
        orderTrack.setStatus(SystemConstant.PAY_STATUS_RESPONSE);
        // 更新订单状态，发送商户通知到消息队列
        afterPay(bankResult);
        orderTrack.setStatus(SystemConstant.PAY_STATUS_RETURN);
        orderTrack.setResult(bankResult.getBankResult());
        return bankResult;
    }

    /**
     * 发送交易至接口
     *
     * @param trade 交易对象
     * @return 银行结果对象
     * @throws PayException 支付异常信息
     */
    private BankResult toPay(Trade trade) throws PayException {
        logger.info(JsonUtils.objectToJsonNonNull(trade));
        // 发送请求至接口
        String result = HttpClientUtils.doPostJson(HttpClientUtils.getHttpClient(), trade.getCoreAcquirer().getUrl(), JsonUtils.objectToJsonNonNull(trade));
        // 处理接口返回信息
        if (StringUtils.isBlank(result)) {
            throw new PayException("Cross请求发生错误！URL：" + trade.getCoreAcquirer().getUrl(), 3001);
        }
        BankResult bankResult = JsonUtils.jsonToPojo(result, BankResult.class);
        if (bankResult != null) {
            return bankResult;
        } else {
            throw new PayException("BankResultJson转BankResult异常", 3002);
        }
    }

    /**
     * 处理交易结果以及更新订单状态
     *
     * @param bankResult 银行结果对象
     */
    private void afterPay(BankResult bankResult) {
        order.setBankOrderId(bankResult.getBankOrderId());
        order.setBankTime(bankResult.getBankTime());
        order.setBankParam(bankResult.getParam());
        order.setBankInfo(bankResult.getBankResult());
        order.setBankStatus(bankResult.getStatus());
        switch (bankResult.getStatus()) {
            case SystemConstant.BANK_STATUS_SUCCESS:
                order.setFinanceStatus(SystemConstant.FINANCE_STATUS_LIQUIDATION);
                order.setExceptionStatus(SystemConstant.EXCEPTION_STATUS_NORMAL);
                break;
            case SystemConstant.BANK_STATUS_FAIL:
                break;
            case SystemConstant.BANK_STATUS_PENDING_PAYMENT:
                break;
            case SystemConstant.BANK_STATUS_UNPAID:
                break;
            case SystemConstant.BANK_STATUS_RESEND:
                break;
            case SystemConstant.BANK_STATUS_TIME_OUT:
                break;
            default:
                break;
        }
        // 更新order信息
        recordService.updateOrder(order);
        // 发送商户通知消息至消息队列
        merchantNotifyService.sendMerchantNotify(order, orderDetail, merchantPay, merchantOrder, acquirerAccount, bankResult);
    }

    /**
     * 获取内嵌返回消息
     *
     * @param bankResult 银行结果对象
     * @return 内嵌返回消息
     */
    private String getInnerReturnMsg(BankResult bankResult) {
        return "tradeNo=" + order.getId() +
                ";billNo=" + order.getMerOrderId() +
                ";billAddress" + "" +
                ";succeed=" + bankResult.getStatus() +
                ";result=" + i18Component.getI18Msg(bankResult.getBankCode(), orderDetail.getLang()) +
                ";md5Info=" + merchantNotifyService.getNotifyMd5Info(order, merchantPay, bankResult) +
                ";remark=" + orderDetail.getRemark() +
                ";currency=" + order.getCurrency() +
                ";amount=" + order.getAmount();
    }

    /**
     * 检查订单是否存在
     *
     * @param merchantId      商户号
     * @param merchantOrderId 商户订单号
     * @throws PayException 支付异常信息
     */
    private void checkOrderExist(Integer merchantId, String merchantOrderId) throws PayException {
        if (transactionService.selectByMerchantIdAndMerchantOrderId(merchantId, merchantOrderId) != null) {
            throw new PayException("商户（" + merchantId + "）订单（" + merchantOrderId + "）已存在！", 2001);
        }
    }
}